#!/usr/bin/env python

"""
Money

a new object type to handle currency.
"""

import types

__revision__ = "1.12"

class Money ( object ):
	"""
	money

	Money is a new object type to handle currency.
	All you have to do to use it in your program is simply:

	>>> import money

	and to create a Money instance just call:

	>>> money.Money.dsep = ","

	>>> money.Money.tsep = "."

	>>> m = money.Money ( '102,23' )

	As you can see, a money value can be represented with a string.
	Please, note the decimal separator here: it is a "," (comma) not a "." (dot)
	You can customize it to your own needs. Simply set:

	>>> m.dsep = ','	# Decimal separator
	>>> m.tsep = '.'	# Thousands separator

	Money objects can be tested agains float values... :

	>>> m == 102.23
	True

	... or against string values (with the same decimal/thousands separators):
	>>> m == '102,23'
	True

	... or against integer values (in this case it will fail):
	>>> m == 102
	False

	They can be converted to string ... :
	>>> str ( m )
	'102,23'

	... or to float ...:
	>>> float ( m )
	102.23

	... or to int (loosing the decimal part):
	>>> int ( m )
	102

	All standard math operations are supported, for example, you can add an int:
	>>> m += 10
	>>> m == 112.23
	True

	But you can also add/sub a string that will be converted to Money before assignment:
	>>> m -= '10,00'
	>>> m == 102.23
	True

	Money constructor accepts almost everything you just want to throw in (ints, floats, strings)
	
	>>> m2 = money.Money ( m )
	>>> m2 == m
	True

	>>> print m2.from_long_int ( '-12630' )
	-126,30
	>>> print m2
	-126,30

	>>> m2 = Money ( '16,40' )
	>>> print m2.to_long_int ()
	1640

	>>> m = Money ()
	>>> x = m.from_long_int ( 833 )
	>>> str ( m )
	'8,33'
	>>> str ( m * 1.2 )
	'10,00'
	>>> disc = 10
	>>> n = Money ( m - ( ( m / 100 ) * disc ) )
	>>> str ( n )
	'7,50'
	>>> n.to_long_int ()
	750

	>>> m = Money ( '1.235,83' )
	>>> str ( m )
	'1.235,83'
	>>> m.to_long_int ()
	123583

	"""


	dsep = "."
	tsep = ","


	def __init__ ( self, val = 0, decs = 2, dsep = -1, tsep = -1 ):
		super ( Money, self ).__init__ ()

		# Decimal separator character
		if dsep != -1:
			self.dsep = dsep
		else:
			self.dsep = Money.dsep

		# Thousands separator character
		if tsep != -1:
			self.tsep = tsep
		else:
			self.tsep = Money.tsep
		
		# Number of decimals in the resulting string
		self.decs = decs

		self.__val = self.__val_rep ( val )

	def to_long_int ( self ):
		s = str ( self ).replace ( self.tsep, "" ) #.__val )
		parts = s.split ( self.dsep )
		result = ''

		if len ( parts ) == 2:
			if len ( parts [ 1 ] ) < self.decs: parts [ 1 ] +=  '0' * self.decs 
			result = parts [ 0 ] + parts [ 1 ] [ : self.decs ]
		else:
			result = parts [ 0 ] + ( '0' * self.decs )

		return int ( result )
		#return int ( self.__val * ( 10 ** self.decs ) )

	def from_long_int ( self, v ):
		self.__val = self.__val_rep ( v ) / ( 10 ** self.decs ) 
		return self
		

	def __str__ ( self ):
		v = self.__val

		if self.__val < 0:
			sign = "-"
			v = abs ( v )
		else:
			sign = ""

		strv = "%s" % int ( ( v * ( 10 ** ( self.decs +1 ) ) ) )
		last_char = int ( strv [ -1 ] )

		try:
			intv = int ( strv [ : -1 ] )
		except ValueError:
			intv = 0

		if last_char > 4: intv += 1

		cents = intv % ( 10 ** self.decs )
		strv  = "%s" % ( intv / ( 10 ** self.decs ) )

		chars = list ( strv [ : ] )
		chars.reverse ()

		count = 0
                dest = ''
                for x in chars:
			count += 1
			if count == 4:
				dest = self.tsep + dest
                                count = 1

			dest = x + dest

		return "%s%s%s%2.2d" % ( sign, dest, self.dsep, cents )

	def __radd__ ( self, v ): return Money ( self.__val + self.__val_rep ( v ) )
	def __add__  ( self, v ): return Money ( self.__val + self.__val_rep ( v ) )
	def __iadd__ ( self, v ):
		self.__val += self.__val_rep ( v )
		return self

	def __rsub__ ( self, v ): return Money ( self.__val - self.__val_rep ( v ) )
	def __sub__  ( self, v ): return Money ( self.__val - self.__val_rep ( v ) )
	def __isub__ ( self, v ):
		self.__val -= self.__val_rep ( v )
		return self

	def __rmul__ ( self, v ): return Money ( self.__val * self.__val_rep ( v ) )
	def __mul__  ( self, v ): return Money ( self.__val * self.__val_rep ( v ) )
	def __imul__ ( self, v ):
		self.__val *= self.__val_rep ( v )
		return self

	def __rdiv__ ( self, v ): return Money ( self.__val / self.__val_rep ( v ) )
	def __div__  ( self, v ): return Money ( self.__val / self.__val_rep ( v ) )
	def __idiv__ ( self, v ):
		self.__val /= self.__val_rep ( v )
		return self

	def __int__ ( self ): return int ( self.__val )
	def __float__ ( self ): return self.__val

	def __eq__ ( self, v ): return ( self.__val ==  self.__val_rep ( v ) )
	def __ne__ ( self, v ): return ( self.__val !=  self.__val_rep ( v ) )

	def __le__ ( self, v ): return ( self.__val <=  self.__val_rep ( v ) )
	def __lt__ ( self, v ): return ( self.__val <   self.__val_rep ( v ) )

	def __gt__ ( self, v ): return ( self.__val >   self.__val_rep ( v ) )
	def __ge__ ( self, v ): return ( self.__val >=  self.__val_rep ( v ) )

	def __hash__ ( self ):    return hash ( str ( self.__val ) )


	def __val_rep ( self, val ):
		"""
		__val_rep ( val ) -> float

		Converts ``val`` into internal representation of the value.
		"""
		if isinstance ( val, Money ): return val.__val
		if isinstance ( val, basestring ):
			if not len ( val ): return float ( 0 )
			return float ( val.replace ( self.tsep, '' ).replace ( self.dsep, '.' ) )
		if isinstance ( val, int ):
			return float ( val )
		if val is None:
			return float ( 0 )

		return float ( val )


if __name__ == '__main__':
	import doctest, money
	fail, tests = doctest.testmod ( money )

	print "Doc Test Result: %d fails on %d tests" % ( fail, tests )
